/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore;

import ore.errors.OrekitException;
import ore.time.AbsoluteDate;
import ore.time.TimeStamped;

import org.apache.commons.math.geometry.Rotation;
import org.apache.commons.math.geometry.Vector3D;

/** 
 * This class is the representation of a complete state holding orbit,
 * attitude and mass information.
 *
 * It contains an {@link Orbit orbital state} at a current
 * {@link AbsoluteDate} both handled by an {@link Orbit}, plus the current
 * mass and attitude.
 * 
 * An instance of this class is immutable.
 * 
 * @see ore.propagation.numerical.NumericalPropagator
 * @author Fabien Maussion
 * @author V&eacute;ronique Pommier-Maurussane
 */
public class SpacecraftState
    extends Object
    implements TimeStamped
{
    private static final double DEFAULT_MASS = 1000.0;

    private static final Attitude DEFAULT_ATTITUDE =
        new Attitude(ore.frames.FramesFactory.getEME2000(), Rotation.IDENTITY, Vector3D.ZERO);


    public final Orbit orbit;
    public final Attitude attitude;
    public final double mass;


    /** 
     * Attitude and mass are set to unspecified values.
     */
    public SpacecraftState(Orbit orbit) {
        this(orbit, DEFAULT_ATTITUDE, DEFAULT_MASS);
    }
    /** 
     * Mass is set to an unspecified value.
     */
    public SpacecraftState(Orbit orbit, Attitude attitude) {
        this(orbit, attitude, DEFAULT_MASS);
    }
    /** 
     * Attitude is set to an unspecified default.
     */
    public SpacecraftState(Orbit orbit, double mass) {
        this(orbit, DEFAULT_ATTITUDE, mass);
    }
    public SpacecraftState(Orbit orbit, Attitude attitude, double mass){
        super();
        if (null != orbit && null != attitude && 0.0 < mass){
            this.orbit    = orbit;
            this.attitude = attitude;
            this.mass     = mass;
        }
        else
            throw new IllegalArgumentException("Null argument");
    }


    public final Attitude getAttitude() {
        return this.attitude;
    }
    public final double getMass() {
        return this.mass;
    }
    public final Orbit getOrbit() {
        return this.orbit;
    }
    public final AbsoluteDate getDate() {
        return this.orbit.getDate();
    }
    public final Frame getFrame() {
        return this.orbit.getFrame();
    }
    /** 
     * Compute the position and velocity of the satellite in orbit
     * definition frame.
     * 
     * <i>Note bene</i>
     * 
     * This method caches its results, and recompute them only when
     * the method is called with a new value for mu. The result is
     * provided as a reference to the internally cached {@link
     * PositionVelocity}, so the caller is responsible to copy it in a
     * separate {@link PositionVelocity} if it needs to keep the
     * value.
     * 
     * @return PV coordinates in orbit definition frame
     */
    public PositionVelocity getPositionVelocity() {
        return this.orbit.getPositionVelocity();
    }
    /** 
     * Compute the position and velocity of the satellite in given
     * output frame. 
     * 
     * <i>Note bene</i>
     * 
     * This method caches its results, and recompute them only when
     * the method is called with a new value for mu. The result is
     * provided as a reference to the internally cached {@link
     * PositionVelocity}, so the caller is responsible to copy it in a
     * separate {@link PositionVelocity} if it needs to keep the
     * value.
     * 
     * @param outputFrame Frame in which coordinates should be defined
     * 
     * @return PV coordinates in argument reference frame
     * 
     * @exception OrekitException If the transformation between frames
     * cannot be computed
     */
    public PositionVelocity getPositionVelocity(Frame outputFrame)
        throws OrekitException
    {
        return this.orbit.getPositionVelocity(outputFrame);
    }
    /** 
     * @return Orbit mu, Central attraction coefficient (m^3/s^2)
     */
    public final double getMu() {
        return this.orbit.getMu();
    }
    /** 
     * The keplerian period is computed directly from semi major axis
     * and central acceleration constant.
     * @return Seconds
     */
    public final double getKeplerianPeriod() {
        return this.orbit.getKeplerianPeriod();
    }
    /** 
     * The keplerian mean motion is computed directly from semi major
     * axis and central acceleration constant.
     * @return Radians per second
     */
    public final double getKeplerianMeanMotion() {
        return this.orbit.getKeplerianMeanMotion();
    }
    /** 
     * @return Meters, orbit semi-major axis
     */
    public final double getA() {
        return this.orbit.getA();
    }
    /** 
     * The first component of the orbit eccentricity vector (per
     * equinoctial parameters).
     * @return e cos(&omega; + &Omega;), first component of
     * eccentricity vector
     * @see #getE()
     */
    public final double getEquinoctialEx() {
        return this.orbit.getEquinoctialEx();
    }
    /** 
     * The second component of the eccentricity vector (per
     * equinoctial parameters).
     * @return e sin(&omega; + &Omega;), second component of the
     * eccentricity vector
     * @see #getE()
     */
    public final double getEquinoctialEy() {
        return this.orbit.getEquinoctialEy();
    }
    /**
     * The first component of the inclination vector (per equinoctial
     * parameters).
     * @return tan(i/2) cos(&Omega;), first component of the
     * inclination vector
     * @see #getI()
     */
    public final double getHx() {
        return this.orbit.getHx();
    }
    /** 
     * The second component of the inclination vector (per equinoctial
     * parameters).
     * @return tan(i/2) sin(&Omega;), second component of the
     * inclination vector
     * @see #getI()
     */
    public final double getHy() {
        return this.orbit.getHy();
    }
    /** 
     * Orbit true latitude argument (per equinoctial parameters).
     * @return Radians, v + &omega; + &Omega; true latitude argument
     * @see #getLE()
     * @see #getLM()
     */
    public final double getLv() {
        return this.orbit.getLv();
    }
    /** 
     * Orbit eccentric latitude argument (per equinoctial parameters).
     * @return Radians, E + &omega; + &Omega; eccentric latitude argument
     * @see #getLv()
     * @see #getLM()
     */
    public final double getLE() {
        return this.orbit.getLE();
    }

    /** 
     * Orbit mean latitude argument (per equinoctial parameters).
     * @return Radians, M + &omega; + &Omega; mean latitude argument
     * @see #getLv()
     * @see #getLE()
     */
    public final double getLM() {
        return this.orbit.getLM();
    }
    /** 
     * @return Orbit eccentricity
     * @see #getEquinoctialEx()
     * @see #getEquinoctialEy()
     */
    public final double getE() {
        return this.orbit.getE();
    }
    /** 
     * @return Radians, orbit inclination
     * @see #getHx()
     * @see #getHy()
     */
    public final double getI() {
        return this.orbit.getI();
    }
    public final int compareTo(TimeStamped stamped){
        return this.orbit.compareTo(stamped);
    }
}
